package manifests

import (
	sredisv1 "git.blindage.org/21h/single-redis-operator/api/v1"

	v1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func GenerateStatefulSet(cr *sredisv1.SingleRedis) v1.StatefulSet {

	// prepare containers for pod
	podContainers := []corev1.Container{}

	// check affinity rules
	affinity := &corev1.Affinity{}
	if cr.Spec.RedisAffinity != nil {
		affinity = cr.Spec.RedisAffinity
	}

	resourcesLimits := corev1.ResourceList{}
	if cr.Spec.RedisPodLimits != nil {
		resourcesLimits = cr.Spec.RedisPodLimits
	}

	resourcesRequests := corev1.ResourceList{}
	if cr.Spec.RedisPodRequests != nil {
		resourcesRequests = cr.Spec.RedisPodRequests
	}

	image := "redis:latest"
	if cr.Spec.Image != "" {
		image = cr.Spec.Image
	}

	volumeMountPath := "/data"
	if cr.Spec.PersistentVolumePath != "" {
		volumeMountPath = cr.Spec.PersistentVolumePath
	}

	redisContainer := corev1.Container{
		Name:    "redis-server",
		Image:   image,
		Command: []string{"redis-server", "/etc/redis.conf"},
		LivenessProbe: &corev1.Probe{
			InitialDelaySeconds: int32(30),
			TimeoutSeconds:      int32(5),
			PeriodSeconds:       int32(10),
			Handler: corev1.Handler{
				Exec: &corev1.ExecAction{
					Command: []string{"/bin/sh", "-c", "redis-cli -h $(hostname) ping"},
				},
			},
		},
		ReadinessProbe: &corev1.Probe{
			InitialDelaySeconds: int32(30),
			TimeoutSeconds:      int32(5),
			PeriodSeconds:       int32(10),
			Handler: corev1.Handler{
				Exec: &corev1.ExecAction{
					Command: []string{"/bin/sh", "-c", "redis-cli -h $(hostname) ping"},
				},
			},
		},
		Env: []corev1.EnvVar{},
		Resources: corev1.ResourceRequirements{
			Requests: resourcesRequests,
			Limits:   resourcesLimits,
		},
		Ports: []corev1.ContainerPort{
			{
				Name:          "redis",
				Protocol:      corev1.ProtocolTCP,
				ContainerPort: 6379,
			},
		},
		VolumeMounts: []corev1.VolumeMount{
			{
				Name:      "data",
				MountPath: volumeMountPath,
			},
			{
				Name:      "config",
				MountPath: "/etc/redis.conf",
				SubPath:   "redis.conf",
			},
		},
	}

	podContainers = append(podContainers, redisContainer)

	podTemplate := corev1.PodTemplateSpec{
		ObjectMeta: metav1.ObjectMeta{
			Labels: MergeLabels(BaseLabels(cr),
				map[string]string{"component": "redis"},
			),
		},
		Spec: corev1.PodSpec{
			Affinity:   affinity,
			Containers: podContainers,
		},
	}

	// override configmap name
	if cr.Spec.Configmap != "" {
		podTemplate.Spec.Volumes = []corev1.Volume{
			{
				Name: "config",
				VolumeSource: corev1.VolumeSource{
					ConfigMap: &corev1.ConfigMapVolumeSource{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: cr.Spec.Configmap,
						},
					},
				},
			},
		}
	} else {
		// use default configmap
		podTemplate.Spec.Volumes = []corev1.Volume{
			{
				Name: "config",
				VolumeSource: corev1.VolumeSource{
					ConfigMap: &corev1.ConfigMapVolumeSource{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: cr.Name,
						},
					},
				},
			},
		}
	}

	replicas := int32(1)

	sts := v1.StatefulSet{
		ObjectMeta: metav1.ObjectMeta{
			Name:      cr.Name,
			Namespace: cr.Namespace,
			Labels: MergeLabels(BaseLabels(cr),
				map[string]string{"component": "redis"},
			),
		},
		Spec: v1.StatefulSetSpec{
			Replicas: &replicas,
			Selector: &metav1.LabelSelector{
				MatchLabels: MergeLabels(BaseLabels(cr),
					map[string]string{"component": "redis"},
				),
			},
			Template: podTemplate,
			UpdateStrategy: v1.StatefulSetUpdateStrategy{
				Type: v1.RollingUpdateStatefulSetStrategyType,
			},
		},
	}

	// if set volume size then create PVC
	if cr.Spec.PersistentVolumeSize != nil {
		PVCTemplate := corev1.PersistentVolumeClaim{
			ObjectMeta: metav1.ObjectMeta{
				Name: "data",
			},
			Spec: corev1.PersistentVolumeClaimSpec{
				AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
				Resources: corev1.ResourceRequirements{
					Requests: corev1.ResourceList{
						corev1.ResourceStorage: *cr.Spec.PersistentVolumeSize,
					},
				},
			},
		}
		if cr.Spec.PersistentVolumeClass != "" {
			PVCTemplate.Spec.StorageClassName = &cr.Spec.PersistentVolumeClass
		}
		if cr.Spec.PersistentVolumePurge {
			PVCTemplate.ObjectMeta.Finalizers = cr.ObjectMeta.Finalizers
		}
		sts.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{PVCTemplate}
	} else {
		// using EmptyDir
		volumeData := corev1.Volume{
			Name: "data",
			VolumeSource: corev1.VolumeSource{
				EmptyDir: &corev1.EmptyDirVolumeSource{},
			},
		}
		sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, volumeData)
	}

	if cr.Spec.RedisNodeSelector != nil {
		sts.Spec.Template.Spec.NodeSelector = *cr.Spec.RedisNodeSelector
	}

	if cr.Spec.RedisTolerations != nil {
		sts.Spec.Template.Spec.Tolerations = *cr.Spec.RedisTolerations
	}

	return sts
}
